import java.util.Arrays;

/**
<p>给你两个按 <strong>非递减顺序</strong> 排列的整数数组&nbsp;<code>nums1</code><em> </em>和 <code>nums2</code>，另有两个整数 <code>m</code> 和 <code>n</code> ，分别表示 <code>nums1</code> 和 <code>nums2</code> 中的元素数目。</p>

<p>请你 <strong>合并</strong> <code>nums2</code><em> </em>到 <code>nums1</code> 中，使合并后的数组同样按 <strong>非递减顺序</strong> 排列。</p>

<p><strong>注意：</strong>最终，合并后数组不应由函数返回，而是存储在数组 <code>nums1</code> 中。为了应对这种情况，<code>nums1</code> 的初始长度为 <code>m + n</code>，其中前 <code>m</code> 个元素表示应合并的元素，后 <code>n</code> 个元素为 <code>0</code> ，应忽略。<code>nums2</code> 的长度为 <code>n</code> 。</p>

<p>&nbsp;</p>

<p><strong>示例 1：</strong></p>

<pre>
<strong>输入：</strong>nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
<strong>输出：</strong>[1,2,2,3,5,6]
<strong>解释：</strong>需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [<em><strong>1</strong></em>,<em><strong>2</strong></em>,2,<em><strong>3</strong></em>,5,6] ，其中斜体加粗标注的为 nums1 中的元素。
</pre>

<p><strong>示例 2：</strong></p>

<pre>
<strong>输入：</strong>nums1 = [1], m = 1, nums2 = [], n = 0
<strong>输出：</strong>[1]
<strong>解释：</strong>需要合并 [1] 和 [] 。
合并结果是 [1] 。
</pre>

<p><strong>示例 3：</strong></p>

<pre>
<strong>输入：</strong>nums1 = [0], m = 0, nums2 = [1], n = 1
<strong>输出：</strong>[1]
<strong>解释：</strong>需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意，因为 m = 0 ，所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
</pre>

<p>&nbsp;</p>

<p><strong>提示：</strong></p>

<ul> 
 <li><code>nums1.length == m + n</code></li> 
 <li><code>nums2.length == n</code></li> 
 <li><code>0 &lt;= m, n &lt;= 200</code></li> 
 <li><code>1 &lt;= m + n &lt;= 200</code></li> 
 <li><code>-10<sup>9</sup> &lt;= nums1[i], nums2[j] &lt;= 10<sup>9</sup></code></li> 
</ul>

<p>&nbsp;</p>

<p><strong>进阶：</strong>你可以设计实现一个时间复杂度为 <code>O(m + n)</code> 的算法解决此问题吗？</p>

<div><div>Related Topics</div><div><li>数组</li><li>双指针</li><li>排序</li></div></div><br><div><li>👍 1815</li><li>👎 0</li></div>
*/

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        backend(nums1, m, nums2, n);
//        twoPointBack(nums1, m, nums2, n);
//        twoPoint(nums1, m, nums2, n);
//        sortArr(nums1, m, nums2, n);
    }

    /**
     * 快速排序
     * 时间复杂度：O((m+n)log(m+n))
     * 空间复杂度：O(log(m+n))
     */
    private void sortArr(int[] nums1, int m, int[] nums2, int n){
        for (int i=m; i<m+n ; i++){
            nums1[i] = nums2[i - m];
        }
        Arrays.sort(nums1);
    }

    /**
     * 双指针
     * 时间复杂度：O(m+n)
     * 空间复杂度：O(m+n)
     */
    private void twoPoint(int[] nums1, int m, int[] nums2, int n){
        int p1 = 0;
        int p2 = 0;
        int[] sorted = new int[m+n];
        int cur = 0;
        while (p1 < m || p2 < n) {
            if(p1 == m){
                // 数组1 到尾部了，返回数组2的当前第一个元素，数组2下标加1
                cur = nums2[p2++];
            } else if(p2 == n){
                // 数组2 到尾部了，返回数组1的当前第一个元素，数组1下标加1
                cur = nums1[p1++];
            } else if(nums1[p1] < nums2[p2]) {
                // 数组1当前头部元素 小于 数组2当前头部元素
                // 返回数组1当前头部元素，数组1下标加1
                cur = nums1[p1++];
            } else {
                // 数组1当前头部元素 大于或等于 数组2当前头部元素
                // 返回数组2当前头部元素，数组2下标加1
                cur = nums2[p2++];
            }
            sorted[p1 + p2 -1] = cur;
        }
        // 覆盖 nums1 数组
        for (int i=0; i!=m+n ; ++i){
            nums1[i] = sorted[i];
        }
    }

    /**
     * 双指针
     * 时间复杂度：O(m+n)
     * 空间复杂度：O(1)
     */
    private void twoPointBack(int[] nums1, int m, int[] nums2, int n){
        int p1 = m-1;
        int p2 = n-1;
        int tail = m + n - 1;
        int cur = 0;
        while (p1 >= 0 || p2 >= 0) {
            if (p1 == -1){
                // 数组1到头了，返回数组2当前值
                cur = nums2[p2--];
            } else if (p2 == -1) {
                // 数组2到头了，返回数组1当前值
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]){
                // 数组1当前值 小于 数组2当前值，返回数组1当前值
                cur = nums1[p1--];
            } else {
                // 数组1当前值 大于或等于 数组2当前值，返回数组2当前值
                cur = nums2[p2--];
            }
            // 从尾部往里放
            nums1[tail--] = cur;
        }
    }

    private void backend(int[] nums1, int m, int[] nums2, int n) {
        int len1 = m - 1;
        int len2 = n - 1;
        int len = m + n - 1;
        while(len1 >= 0 && len2 >= 0) {
            // 注意--符号在后面，表示先进行计算再减1，这种缩写缩短了代码
            nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];
        }
        // 表示将nums2数组从下标0位置开始，拷贝到nums1数组中，从下标0位置开始，长度为len2+1
        System.arraycopy(nums2, 0, nums1, 0, len2 + 1);
    }
}
//leetcode submit region end(Prohibit modification and deletion)
